package com.sapitest.utils.linux;

import com.jcraft.jsch.*;
import com.sapitest.utils.ConfigFile;
import com.sapitest.utils.bean.SSHInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.Reporter;

import java.io.*;
import java.util.concurrent.atomic.AtomicReference;

/**
 * 通过SSH2远程Linux执行命令工具类
 * @date 2019-01-13
 * @author Liuxu
 * @version 1.0.1
 * @since jdk1.8
 */
public class LinuxUtil {
    private static final Logger logger = LoggerFactory.getLogger(LinuxUtil.class);

    private static int SESSION_TIMEOUT = Integer.valueOf(ConfigFile.getSystemParameter("session_timeout"));
    private static int CHANNEL_TIMEOUT = Integer.valueOf(ConfigFile.getSystemParameter("channel_timeout"));
    private static int WAIT_TIME = Integer.valueOf(ConfigFile.getSystemParameter("wait_time"));
    private Session session;

    private LinuxUtil(SSHInfo sshInfo) throws JSchException{
        JSch jsch = new JSch();
        session = jsch.getSession(sshInfo.getUser(),sshInfo.getHost(),sshInfo.getPort());
        session.setPassword(sshInfo.getPassword());
        session.setUserInfo(new MyUserInfo());
        session.connect(SESSION_TIMEOUT);
    }

    /**
     * 实例化对象LinuxUtil
     * @param user  用户名
     * @param password  密码
     * @param host  主机IP
     * @param port  端口，默认22
     * @return  LinuxUtil
     */
    public static LinuxUtil newInstance(String user, String password, String host, int port) {
        try {
            SSHInfo sshInfo = new SSHInfo(user, password, host, port);
            return new LinuxUtil(sshInfo);
        } catch (JSchException e) {
            logger.error(e + "  Linux连接异常");
        }
        return null;
    }

    /**
     * 使用shell频道输出结果到本地
     * @param cmd   命令
     * @param outputFileName    执行结果输出的本地文件路径
     * @return  执行完成返回"执行成功"，失败返回"执行失败"
     */
    public String shell(String cmd, String outputFileName) {
        Channel channel = null;
        try (PipedInputStream pipeIn = new PipedInputStream(); PipedOutputStream pipeOut = new PipedOutputStream(pipeIn); FileOutputStream fileOut = new FileOutputStream(outputFileName, true)) {
            channel =  session.openChannel("shell");
            channel.setInputStream(pipeIn);
            channel.setOutputStream(fileOut);
            channel.connect(CHANNEL_TIMEOUT);
            pipeOut.write(cmd.getBytes());
            Thread.sleep(WAIT_TIME);
            logger.info("使用shell频道执行linux命令成功。输出路径：" + outputFileName);
            return "OK";
        } catch (JSchException e) {
            logger.error(e + "Linux连接异常");
        } catch (IOException e) {
            logger.error(e + "执行Linux命令时IO异常");
        } catch (InterruptedException e) {
            logger.error(e + "执行Linux命令时线程异常");
        } finally {
            if (channel != null) {
                channel.disconnect();
            }
            logger.info("使用shell频道执行linux命令完成，断开连接");
        }
        logger.info("使用shell频道执行linux命令失败。");
        return "Failed";
    }

    /**
     * 使用exec频道输出shell命令执行结果
     * @param cmd   命令
     * @return  返回执行结果
     */
    public String exec(String cmd) {
        String result = "";
        ChannelExec channelExec = null;
        try {
            channelExec = (ChannelExec)session.openChannel( "exec" );
            channelExec.setCommand(cmd);
            channelExec.setInputStream(null);
            channelExec.setErrStream(System.err);
            InputStream in = channelExec.getInputStream();
            channelExec.connect();

            AtomicReference<StringBuffer> sb = new AtomicReference<>(new StringBuffer(1024));
            byte[] tmp = new byte[1024];
            Thread.sleep(WAIT_TIME);
            while (in.available() > 0) {
                int i = in.read( tmp, 0, 1024 );
                if (i < 0) {
                    break;
                }
                sb.get().append(new String(tmp,0, i));
                result = sb.toString();
            }
        } catch (JSchException e) {
            logger.error(e + "Linux连接异常");
        } catch (IOException e) {
            logger.error(e + "执行Linux命令时IO异常");
        } catch (InterruptedException e) {
            logger.error(e + "执行Linux命令时线程异常");
        } finally {
            if (channelExec != null) {
                channelExec.disconnect();
            }
            logger.info("使用exec频道执行linux命令完成，断开连接");
        }
        logger.info("使用exec频道执行linux命令成功，结果：" + result);
        return result;
    }

    private Session getSession(){
        return session;
    }

    /**
     * 关闭连接
     */
    public void close(){
        getSession().disconnect();
    }

}
